#pragma once
#include <QString>
#include <QIcon>
#include <QUuid>
#include <QDateTime>
#include <QFile>
#include <QFileInfo>
// #include <QDebug>

// 创建命名空间
// 约定：仅限于当前项目
// 如果代码所在的文件是在项目顶层目录中，此时就直接使用全局命名空间（不手动指定）如main.cpp,mainwidget.cpp
// 如果代码所在文件，在某个子目录中，如model,data.h，此时我们就指定一个和目录名字相同的命名空间
// 使代码中的命名空间的结构和文件在目录中的结构一致，尤其是在目录结构更复杂嵌套多层

namespace model
{




///////////////////////////////////////////
// 工具函数，后续很多模块可能使用
//////////////////////////////////////////
// 封装一个宏来打印日志
// 格式字符串的方式去构造

// __FILE__ 获取的路径太长，单独写一个函数获取文件名
static inline QString getFileName(const QString& path)
{
    // 操作文件属性的对象 fileinfo
    QFileInfo fileInfo(path);
    return fileInfo.fileName();
}

// 需要给定命名空间，否则找不到该函数
#define TAG QString("[%1:%2]").arg(model::getFileName(__FILE__),QString::number(__LINE__))
// qDebug 打印字符串时 自带"" ,使用noquote 不带""
#define LOG qDebug().noquote() << TAG


// 要求函数定义如果写在.h中，必须加static,或者inline ，两者都加也可以
// 防止同一个函数被 多个.h重复包含，导致链接阶段出现 函数重定义的问题
// timestamp 时间戳
static inline QString formatTime(int64_t timestamp)
{
    // 1.先把时间戳转化为QDateTime对象
    QDateTime dateTime = QDateTime::fromSecsSinceEpoch(timestamp);
    // 2. 把对象转成 格式化时间

    // toString 提供特定的格式，M月，d天，H小时，m分钟，s秒
    return dateTime.toString("MM-dd HH:mm:ss");

}

// 通过这个函数得到秒级的时间 --> 时间戳
static inline int64_t getTime()
{

    return QDateTime::currentSecsSinceEpoch();
}



// 根据 QByteArray,转成 QIcon
static inline QIcon makeIcon(const QByteArray& byteArray)
{
    // 通过中间对象来完成转换
    // 使用QPixmap提供的loadFromData 转成图片属性
    // 再利用QIcon 提供的构造完成
    QPixmap pixmap;
    pixmap.loadFromData(byteArray);
    QIcon icon(pixmap);
    return icon;
}

// 读写文件操作
// 从指定文件中读取所有的二进制内容 得到一个QByteArry

static inline QByteArray loadFileToByteArry(const QString& path)
{
    QFile file(path);
    bool ok = file.open(QFile::ReadOnly);
    if(!ok)
    {
        // 打开文件失败
        qDebug() << "文件打开失败\n";
        return QByteArray();
    }
    QByteArray content = file.readAll();
    file.close();
    return content;
}

// 把QByteArray 中的内容，写入到某个指定文件中
static inline void writeByteArryToFile(const QString& path,const QByteArray& content)
{
    QFile file(path);
    bool ok = file.open(QFile::WriteOnly);
    if(!ok)
    {
        qDebug() << "文件打开失败\n";
        return ;
    }
    file.write(content);
    // 刷新缓冲区!
    file.flush();
    file.close();
}








// 显眼的表示
///////////////////////////////////////////
//用户信息
//////////////////////////////////////////
class UserInfo{
public:
    //字符串作为ID作为ID，可以有更灵活的方式来生成
    QString userId = "";        // 用户编号
    QString nickname = "";      // 用户昵称
    QString description = "";   // 用户签名 -->个性签名
    QString phone = "";         // 手机号码， 用手机号码登录
    QIcon avatar;               // 用户头像

};
///////////////////////////////////////////
//消息信息
//////////////////////////////////////////
enum MessageType
{
    TEXT_TYPE,  //文本消息
    IMAGE_TYPE, //图片消息
    FILE_TYPE,  // 文件消息
    SPEECH_TYPE // 语音消息
};
class Message
{
public:
    QString mseeageId = "";
    // 会话和消息之间属于 一对多 这样的关系
    // 消息内加入 会话id，通过会话id来识别出 该类消息是属于哪些会话的
    QString chatSessionId = "";          // 会话id
    QString time = "";                   // 消息时间，格式化时间    06-07 10:00:00
    MessageType messageType ;        // 消息类型
    UserInfo sender;                // 发送者信息
    QByteArray content;             // 消息正文内容 如果是文本消息，正文是字符串，如果是图片，文件语音信息，正文是一个二进制序列，二进制序列用QByteArray来表示
    QString fileId = "";                 // 文件身份标识，当消息为图片消息，语音消息，文件时该字段才生效，如果为文本消息，该字段为空
    QString fileName = "";               // 文件名称，只是当消息类型为文件消息，其他消息为空。图片消息和语音消息是不需要显示到界面上的，
                                    // 对于文件消息，我们可以点击另存为这样的操作

    // 工厂方法 构造Message

    // extraInfo 目前只是在消息类型为文件消息时，作为 "文件名" 补充，可扩展为其他含义
    static Message makeMessage(MessageType messageType,const QString& chatSessionId, const UserInfo sender,
                               const QByteArray& content, const QString& extraInfo)
    {
        if(messageType == TEXT_TYPE)
        {
            return makeTextMessage(chatSessionId,sender,content);
        }
        else if(messageType == IMAGE_TYPE)
        {
            return makeImageMessage(chatSessionId,sender,content);
        }
        else if(messageType == FILE_TYPE)
        {
            return makeFileMessage(chatSessionId,sender,content,extraInfo);
        }
        else if(messageType == SPEECH_TYPE)
        {
            return makeSpeechMessage(chatSessionId,sender,content);
        }
        else
        {
            // 触发未知消息类型
            return Message();
        }

    }

private:
    // 通过这个方法生成唯一的messageId
    static QString makeId()
    {
        // sliced (pos,len)
        // "M" 表示消息
        return "M" + QUuid::createUuid().toString().sliced(25,12);
    }

    static Message makeTextMessage(const QString& chatSessionId, const UserInfo sender,const QByteArray& content)
    {
        Message message;
        // 此处需要确保设置的 messageId 是 "唯一"的
        // 在分布式系统上也能保证它的唯一性
        message.mseeageId = makeId();
        message.chatSessionId = chatSessionId;
        message.sender = sender;
        message.time = formatTime(getTime()); // 生成一个格式化时间
        message.content = content;
        message.messageType = TEXT_TYPE;
        // 对于文本消息来说这两个属性不用
        message.fileId = "";
        message.fileName = "";
        return message;
    }
    //
    static Message makeImageMessage(const QString& chatSessionId, const UserInfo sender,const QByteArray& content)
    {
        Message message;
        // 此处需要确保设置的 messageId 是 "唯一"的
        // 在分布式系统上也能保证它的唯一性
        message.mseeageId = makeId();
        message.chatSessionId = chatSessionId;
        message.sender = sender;
        message.time = formatTime(getTime()); // 生成一个格式化时间
        message.content = content;
        message.messageType = IMAGE_TYPE;
        // fileId 后续使用再进一步设置
        message.fileId = "";
        // fileName 不使用直接设置为 ""
        message.fileName = "";
        return message;
    }

    static Message makeFileMessage(const QString& chatSessionId, const UserInfo sender,const QByteArray& content,const QString& filename)
    {
        Message message;
        // 此处需要确保设置的 messageId 是 "唯一"的
        // 在分布式系统上也能保证它的唯一性
        message.mseeageId = makeId();
        message.chatSessionId = chatSessionId;
        message.sender = sender;
        message.time = formatTime(getTime()); // 生成一个格式化时间
        message.content = content;
        message.messageType = FILE_TYPE;
        // fileId 后续使用再进一步设置
        message.fileId = "";
        message.fileName = filename;
        return message;
    }

    static Message makeSpeechMessage(const QString& chatSessionId, const UserInfo sender,const QByteArray& content)
    {
        Message message;
        // 此处需要确保设置的 messageId 是 "唯一"的
        // 在分布式系统上也能保证它的唯一性
        message.mseeageId = makeId();
        message.chatSessionId = chatSessionId;
        message.sender = sender;
        message.time = formatTime(getTime()); // 生成一个格式化时间
        message.content = content;
        message.messageType = SPEECH_TYPE;
        // fileId 后续使用再进一步设置
        message.fileId = "";
        // 不使用
        message.fileName = "";
        return message;
    }





};

///////////////////////////////////////////
//用户聊天会话
//////////////////////////////////////////
class ChatSessionInfo{
public:
    QString chatSessionId = "";     // 会话编号
    QString chatSessionName = "";   // 会话名字，如果是单聊，会话名字是用户名字，如果是群名，则会话名字自定义
    Message lastMessage;            // 表示最新的消息,这个内容就是为了在会话列表中能够起到 显示-提示 这样的效果
    QIcon avatar;                   //会话头像，如果是单聊 会话头像为对方头像，如果是群聊，会话头像是自定义
    QString userid = "";            // 针对会话信息来说，一个会话里其实可以包含多个用户，这个会话里具体包含哪些用户，后续通过单独的方式来管理
                                    // 如果是单聊， userid 表示对方的id， 如果是群聊，userid设为空，后续通过其他方式来把完整的用户id列表拿到
                                    // 可以区分是单聊还是群聊

};



} // end model
